// The derived classes for the ptwrap library

#include "ptwrap.hpp"

namespace pw // POSIX wrapped
{

// [[ex03da]] 
// DEBUG descr The semaphore value returned by the signal method.
class semaphore
{
public:
// DEBUG from
	semaphore(unsigned initval) :
		restcond_(leadcond_), value_(initval), leadn_(0)
	{ }

	int wait(unsigned n = 1)
	{
		pw::lockmutex lm(leadcond_);
		while(leadn_) 
			restcond_.wait();

		leadn_ = n;
		while(value_ < n)
			leadcond_.wait();
		value_ -= n;
		leadn_ = 0;
		restcond_.signal();
		return 0;
	}
	int trywait(unsigned n = 1)
	{
		pw::lockmutex lm(leadcond_);
		if(leadn_ || value_ < n)
			return ETIMEDOUT;
		value_ -= n;
		return 0;
	}
	int timedwait(unsigned n, 
			const struct timespec &abstime)
	{
		pw::lockmutex lm(leadcond_);
		while(leadn_) {
			if (restcond_.timedwait(abstime) == ETIMEDOUT)
				return ETIMEDOUT;
		}

		leadn_ = n;
		while(value_ < n) {
			if (leadcond_.timedwait(abstime) == ETIMEDOUT) {
				leadn_ = 0;
				restcond_.signal();
				return ETIMEDOUT;
			}
		}
		value_ -= n;
		leadn_ = 0;
		restcond_.signal();
		return 0;
	}
// DEBUG to
	// ...
	int signal(unsigned n = 1)
	{
		pw::lockmutex lm(leadcond_);
		value_ += n;
		if (leadn_ && value_ >= leadn_)
			leadcond_.signal();
		return value_;
	}
	// ...
// DEBUG from

protected:
	// the lead thread waits on it
	pw::pmcond leadcond_; 
	// the rest of the threads wait on this one
	pw::pchaincond restcond_; 
	// current value of the semaphore, always positive
	unsigned value_; 
	// value the lead thread is waiting for, or 0
	unsigned leadn_; 
// DEBUG to
};
// END


// [[ex03ae]]
// DEBUG descr Auto-reset event with both pulse and timed wait.
class autoevent
{
public:
	autoevent(bool signaled = false) :
		signaled_(signaled), evsleepers_(0)
	{ }

	int wait()
	{
		pw::lockmutex lm(cond_);
		++evsleepers_;
		while (!signaled_)
			cond_.wait();
		--evsleepers_;
		signaled_ = false;
		return 0;
	}
	int trywait()
	{
		pw::lockmutex lm(cond_);
		if (!signaled_)
			return ETIMEDOUT;
		signaled_ = false;
		return 0;
	}
	int timedwait(const struct timespec &abstime)
	{
		pw::lockmutex lm(cond_);
		++evsleepers_;
		while (!signaled_) {
			if (cond_.timedwait(abstime) == ETIMEDOUT) {
				--evsleepers_;
				if (signaled_) {
					signaled_ = false;
					return 0;
				} else {
					return ETIMEDOUT;
				}
			}
		}
		--evsleepers_;
		signaled_ = false;
		return 0;
	}
	int signal()
	{
		pw::lockmutex lm(cond_);
		signaled_ = true;
		cond_.signal();
		return 0;
	}
	int reset()
	{
		pw::lockmutex lm(cond_);
		signaled_ = false;
		return 0;
	}
	int pulse()
	{
		pw::lockmutex lm(cond_);
		if (evsleepers_ > 0) {
			signaled_ = true;
			cond_.signal();
		} else {
			signaled_ = false;
		}
		return 0;
	}

protected:
	// contains both condition variable and a mutex
	pw::pmcond cond_; 
	// semaphore has been signaled
	bool signaled_; 
	// the counter of waiting threads
	int evsleepers_; 
};
// END

// [[ex03ca]]
// DEBUG descr The general hold.
class hold 
{
public:
	hold() : count_(0)
	{ }
	
	void acquire() 
	{
		pw::lockmutex lm(cond_);
		++count_;
	}

	// returns the number of holds left
	int release() 
	{
		pw::lockmutex lm(cond_);
		if (--count_ <= 0)
			cond_.broadcast();
		return count_;
	}

	void wait()
	{
		pw::lockmutex lm(cond_);
		while(count_ != 0) 
			cond_.wait();
	}

	int get_hold_count()
	{
		pw::lockmutex lm(cond_);
		return count_;
	}

protected:
	// signaled when count_ drops to 0
	pw::pmcond cond_; 
	// count of holds
	int count_; 
};
// END

// fragment of ex03ch
class barrier 
{
public:
	barrier(int count) :
		flip_(0), flop_(0), count_(count)
	{ }

	// May be called only by the control thread, to
	// adjust the number of workers
	int add_workers(int add)
	{
		count_ += add; // add may be negative
		return count_;
	}

	// Workers call this method to synchronize on the
	// barrier
	void sync()
	{
		flip_.signal(1);
		flop_.wait(1);
	}

	// Control calls this method to wait for the barrier
	// to be reached by all the workers
	void wait()
	{
		flip_.wait(count_);
	}

	// Control calls this method to release the workers
	// from the barrier
	void signal()
	{
		flop_.signal(count_);
	}

protected:
	// control waits for workers
	pw::semaphore flip_; 
	// workers wait for control
	pw::semaphore flop_; 
	// number of threads synchronized by the barrier
	int count_; 
};
// END

// [[ex03ia]]
// DEBUG descr Split lock single-stepping with keeping track of the worker thread intent.
class splitlock
{
public:
	// Information about whether the worker will enter
	// the critical section
	enum worker_intent {
		WORKER_WILL_ENTER, // it definitely will
		WORKER_WONT_ENTER, // it definitely won't
		WORKER_MIGHT_ENTER // unknown
	};

	// @param safe - flag: if true, place the lock
	// initially into a safe state, so that the worker
	// thread may not enter it until the control thread
	// releases it; 
	// if false, place it into the unsafe state and
	// allow the worker thread to enter it freely
	splitlock(bool safe = false) :
		cond_safe_(cond_enter_),
		cond_worker_(cond_enter_),
		wi_(WORKER_MIGHT_ENTER),
		may_enter_(!safe),
		is_safe_(safe),
		may_step_(false)
	{ }

	// Control thread side of API.

	// ... the rest of control side is unchanged, so it's
	// omitted here ...
	// DEBUG {
	void request_safe()
	{
		pw::lockmutex lm(cond_enter_);
		may_enter_ = false;
	}

	void wait_safe()
	{
		pw::lockmutex lm(cond_enter_);
		while (!is_safe_ || may_step_) {
			cond_safe_.wait();
		}
	}

	// A convenience combination - just in case
	void lock_safe()
	{
		request_safe();
		wait_safe();
	}

	void release_safe()
	{
		pw::lockmutex lm(cond_enter_);
		may_enter_ = true;
		cond_enter_.broadcast();
	}

	// Returns true if a thread was waiting and is now
	// allowed to step, or false if there was no thread
	// waiting.
	bool release_safe_step()
	{
		pw::lockmutex lm(cond_enter_);
		if (cond_enter_.sleepers_) {
			may_step_ = true;
			cond_enter_.broadcast();
			return true;
		} else {
			return false;
		}
	}
	// DEBUG }

	// Wait until worker tries to enter the critical
	// section, or if it's unknown whether it will, then
	// wait no longer than until the specified time.  May
	// be called only when the splitlock is in safe
	// condition.  Returns true if the wait succeeded,
	// false if timed out.
	// @param abstime - time limit 
	// @param timed - if false, the time limit applies
	//   only if it's unknown whether the worker will
	//   enter, if true, the time limit applies even if
	//   it's known that the worker will enter
	bool wait_for_worker(const struct timespec &abstime, 
		bool timed = false)
	{
		int res;

		pw::lockmutex lm(cond_enter_);
		while(true) {
			if (cond_enter_.sleepers_ > 0)
				// worker is already trying to enter
				return true; 

			switch(wi_) {
			case WORKER_WILL_ENTER:
				if (timed) {
					res = cond_worker_.timedwait(abstime);
					if (res == ETIMEDOUT)
						return false;
				} else {
					cond_worker_.wait();
				}
				break;
			case WORKER_WONT_ENTER:
				return false;
			case WORKER_MIGHT_ENTER:
				res = cond_worker_.timedwait(abstime);
				if (res == ETIMEDOUT 
				&& wi_ == WORKER_MIGHT_ENTER)
					return false;
				// else something definite became known,
				// parse this knowledge on the next
				// iteration
				break;
			};
		}
	}

	worker_intent get_worker_intent()
	{
		pw::lockmutex lm(cond_enter_);
		return wi_;
	}

	// Worker thread side of API.

	void enter()
	{
		pw::lockmutex lm(cond_enter_);
		while (!may_enter_ && !may_step_) {
			cond_worker_.broadcast();
			cond_enter_.wait();
		}
		is_safe_ = false;
		may_step_ = false;
		wi_ = WORKER_MIGHT_ENTER;
	}

	void leave()
	{
		pw::lockmutex lm(cond_enter_);
		is_safe_ = true;
		cond_safe_.broadcast();
	}

	// Try to enter the critical section.
	// Never blocks.
	// Returns true on success, false if the splitlock
	// is already safe.
	bool try_enter()
	{
		pw::lockmutex lm(cond_enter_);
		if (!may_enter_ && !may_step_)
			return false; 
		is_safe_ = false;
		may_step_ = false;
		wi_ = WORKER_MIGHT_ENTER;
		return true;
	}

	// After an enter attempt has failed and the
	// caller has freed all the sensitive resources,
	// block until the control thread releases the
	// lock and another attempt to enter may be made.
	void continue_enter()
	{
		pw::lockmutex lm(cond_enter_);
		while (!may_enter_ && !may_step_) {
			cond_worker_.broadcast();
			cond_enter_.wait();
		}
	}

	void set_worker_intent(worker_intent wi)
	{
		pw::lockmutex lm(cond_enter_);
		wi_ = wi;
		cond_worker_.broadcast();
	}

protected:
	// signaled when the worker thread is allowed to
	// enter
	pw::pmcond cond_enter_; 
	// signaled when the control thread becomes safe
	pw::pchaincond cond_safe_; 
	// signaled when wi_ changes or the worker sleeps
	// on enter
	pw::pchaincond cond_worker_; 
	worker_intent wi_;
	// flag: the worker thread may enter
	bool may_enter_; 
	// flag: the control thread is safe (working
	// thread has not entered)
	bool is_safe_; 
	// flag: the worker thread may enter once
	bool may_step_; 
};
// END

}; // pw
